﻿using Dotnet.Utils.Utility.AssemblyUtil;
using Dotnet.Utils.Utility.ObjectExtensions.Obj;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;

namespace Dotnet.Utils.Utility.Other
{
    /// <summary>
    /// 扩展枚举
    /// </summary>
    public static class EnumExtensions
    {
        #region 获取枚举的Description值


        /// <summary>
        /// 获取枚举的Description值
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string GetDescription(Enum value)
        {
            return value.GetAtrribute<DescriptionAttribute>()?.Description ?? "";
        }
        public static string GetDescription<TEnum>(this TEnum value) where TEnum : Enum
        {
            return value.GetType().GetMember(value.ToString()).FirstOrDefault()?.GetCustomAttribute<DescriptionAttribute>()?
                .Description ?? value.ToString();
        }
        private static string GetDescription(this MemberInfo memberInfo)
        {
            DescriptionAttribute[] attributes = (DescriptionAttribute[])memberInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
            return attributes.Any() ? attributes[0].Description : memberInfo.Name;
        }
        #endregion

        #region 获取枚举的数组

        /// <summary>
        /// 获取枚举值数组
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string[] GetEnumArray(this Type type)
        {
            return Enum.GetNames(type);
        }

        /// <summary>
        /// 获取枚举值数组
        /// </summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <returns></returns>
        public static int[] GetEnumNumArray<TEnum>() where TEnum : Enum
        {
            return Enum.GetValues(typeof(TEnum)).Cast<int>().ToArray();
        }

        /// <summary>
        /// 获取枚举值字符串数组
        /// </summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <returns></returns>
        public static string[] GetEnumArray<TEnum>() where TEnum : Enum
        {
            return Enum.GetNames(typeof(TEnum));
        }

        /// <summary>
        /// 将枚举值以列表的形式呈现
        /// </summary>
        public static IEnumerable<T> Split<T>(this T enums) where T : notnull
        {
            return Enum.GetValues(enums.GetType()).Cast<T>();
        }

        /// <summary>
        /// 获取枚举描述数组
        /// </summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <param name="enumType"></param>
        /// <returns></returns>
        public static string[] GetDescriptionArray<TEnum>(this TEnum enumType) where TEnum : Enum
        {
            return typeof(TEnum).GetFields(BindingFlags.Static | BindingFlags.Public).Select(field => field.GetDescription()).ToArray();
        }

        /// <summary>
        /// 获取枚举描述数组
        /// </summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <param name="enumType"></param>
        /// <returns></returns>
        public static string[] GetDescriptionArray(this Type enumType)
        {
            return enumType.GetFields(BindingFlags.Static | BindingFlags.Public).Select(field => field.GetDescription()).ToArray();
        }
        #endregion

        /// <summary>
        /// 枚举排序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static IEnumerable<T> OrderByEnum<T>(this IEnumerable<T> source) where T : Enum
        {
            var orderedValuesWithoutAttributeArray = source.Where(x => !x.GetType().GetField(x.ToString()).GetCustomAttributes(false)
                    .OfType<EnumOrderAttribute>().Any()).OrderBy(x => (int)(object)x);
            var orderedValuesWithAttributeArray = source.Except(orderedValuesWithoutAttributeArray)
            .OrderBy(x => x.GetType().GetField(x.ToString()).GetCustomAttributes(false).OfType<EnumOrderAttribute>().FirstOrDefault().Order).ThenBy(x => (int)(object)x);
            return orderedValuesWithAttributeArray.Concat(orderedValuesWithoutAttributeArray);
        }

        /// <summary>
        /// 枚举倒序排序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static IEnumerable<T> OrderByDescEnum<T>(this IEnumerable<T> source) where T : Enum
        {
            var orderedValuesWithoutAttributeArray = source.Where(x => !x.GetType().GetField(x.ToString()).GetCustomAttributes(false)
                    .OfType<EnumOrderAttribute>().Any()).OrderBy(x => (int)(object)x);
            var orderedValuesWithAttributeArray = source.Except(orderedValuesWithoutAttributeArray)
            .OrderBy(x => x.GetType().GetField(x.ToString()).GetCustomAttributes(false).OfType<EnumOrderAttribute>().FirstOrDefault().Order).ThenBy(x => (int)(object)x);
            return orderedValuesWithoutAttributeArray.Concat(orderedValuesWithAttributeArray);
        }
        /// <summary>
        /// 枚举排序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T[] OrderByEnum<T>() where T : Enum
        {
            return Enum.GetValues(typeof(T)).Cast<T>().OrderByEnum().ToArray();
        }
        /// <summary>
        /// 枚举倒序排序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T[] OrderByDescEnum<T>() where T : Enum
        {
            return Enum.GetValues(typeof(T)).Cast<T>().OrderByDescEnum().ToArray();
        }

        /// <summary>
        /// 获取Category
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string GetCategory(this Enum value)
        {
            return value.GetAtrribute<CategoryAttribute>()?.Category ?? "";
        }

        /// <summary>
        /// 获取枚举特性
        /// </summary>
        /// <typeparam name="TAttribute"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static TAttribute GetAtrribute<TAttribute>(this Enum value) where TAttribute : Attribute
        {
            var fieldInfo = value.GetType().GetField(value.ToString());
            if (fieldInfo.IsDefined(typeof(TAttribute)) == false)
            {
                return null;
            }

            return fieldInfo.GetCustomAttribute<TAttribute>();
        }


        /// <summary>
        /// 获取枚举类的描述值
        /// </summary>
        /// <param name="enumValue"></param>
        /// <returns></returns>
        public static string GetEnumCalssDescription(Type enumType)
        {
            return AttributeUtil.GetClassDescription(enumType);
        }

        /// <summary>
        /// 获取枚举的 Description 值
        /// </summary>
        /// <param name="enumValue"></param>
        /// <returns></returns>
        public static string GetEnumDescription(Enum enumValue)
        {
            FieldInfo field = enumValue.GetType().GetField(enumValue.ToString());
            ////获取描述属性
            //var attributes = (DescriptionAttribute[])field.GetCustomAttributes(typeof(DescriptionAttribute), false);
            //return (attributes != null && attributes.Length > 0) ? attributes[0].Description : enumValue.ToString();

            DescriptionAttribute descriptionAttribute = field.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault() as DescriptionAttribute;
            return descriptionAttribute == null ? enumValue.ToString() : descriptionAttribute.Description;
        }

        /// <summary>
        /// 枚举转字典
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static Dictionary<int, string> GetEnumDictionary<TEnum>() where TEnum : struct, Enum
        {
            Dictionary<int, string> dict = Enum.GetValues(typeof(TEnum))
             .Cast<TEnum>().ToDictionary(key => Convert.ToInt32(key), value => value.ToString());
            return dict;
        }

        /// <summary>
        /// 枚举转字典
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static Dictionary<int, string> GetEnumDictionary(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }
            if (!type.IsEnum)
            {
                throw new ArgumentException("Type参数必须是一个枚举类型");
            }
            Dictionary<int, string> dict = Enum.GetValues(type)
                       .Cast<Enum>().ToDictionary(key => Convert.ToInt32(key), value => value.ToString());
            return dict;
        }

        /// <summary>
        /// 获取所有枚举和描述值集合
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static Dictionary<int, string> GetEnumDescriptionDictionary<TEnum>() where TEnum : struct, Enum
        {
            return GetEnumDescriptionDictionary(typeof(TEnum));
        }

        /// <summary>
        /// 获取所有枚举和描述值集合
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static Dictionary<int, string> GetEnumDescriptionDictionary(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }
            if (!type.IsEnum)
            {
                throw new ArgumentException("Type参数必须是一个枚举类型");
            }
            Dictionary<int, string> dict = new Dictionary<int, string>();
            foreach (var value in Enum.GetValues(type))
            {
                int key = (int)value;
                string name = Enum.GetName(type, value);
                string description = type.GetField(name).GetDescription();
                var dicValue = !string.IsNullOrEmpty(description) ? description : name;
                dict.Add(key, dicValue);
            }
            return dict;
        }

        /// <summary>
        /// 枚举值转枚举集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enumValue"></param>
        /// <returns></returns>
        public static List<T> ConvertToEnums<T>(int enumValue) where T : struct, IConvertible
        {
            if (!typeof(T).IsEnum)
            {
                throw new Exception("T必须是一个枚举类型");
            }

            var enums = new List<T>();
            foreach (int csValue in Enum.GetValues(typeof(T)))
            {
                if ((csValue & enumValue) == csValue)
                {
                    var controlStage = (T)Enum.Parse(typeof(T), csValue.ToString());
                    enums.Add(controlStage);
                }
            }
            return enums;
        }

        /// <summary>
        /// 枚举集合转枚举
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enums"></param>
        /// <returns></returns>
        public static int ConvertEnumsToValue<T>(List<T> enums)
              where T : struct, IConvertible
        {
            int controlStageValue = 0;
            if (!typeof(T).IsEnum)
            {
                throw new Exception("T必须是一个枚举类型");
            }
            enums.ForEach(c =>
            {
                object obj = c;
                int nValue = (int)obj;
                controlStageValue = controlStageValue | nValue;
            });
            return controlStageValue;
        }

        #region Flags

        /// <summary>
        /// 模拟Flags数据
        /// </summary>
        [Flags]
        public enum Option
        {
            //All = -1,
            //None = 0,
            Option = 1 << 0,//1
            Option1 = 1 << 1,//2
            Option2 = 1 << 2,//4
            Option3 = 1 << 3,//8
            Option4 = 1 << 4,//16
            Option5 = 1 << 5,//32
            Option6 = 1 << 6,//64
            Option7 = 1 << 7,//128
        }

        /// <summary>
        /// 枚举数组转 flags
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="values"></param>
        /// <returns></returns>
        public static T ValuesToFlags<T>(this IEnumerable<T> values) where T : Enum
        {
            int result = values.Sum(value => Convert.ToInt32(value));
            return (T)Enum.ToObject(typeof(T), result);
        }

        /// <summary>
        /// flags 转枚举数组
        /// </summary>
        public static IEnumerable<T> FlagsToValues<T>(T value) where T : Enum
        {
            return Enum.GetValues(typeof(T)).Cast<T>().Where(flag => value.HasFlag(flag));
        }

        /// <summary>
        /// 传入枚举值判断是否包含某个枚举
        /// </summary>
        /// <param name="flags"></param>
        /// <param name="enumValue"></param>
        /// <returns></returns>
        public static bool HasFlag<TEnum>(TEnum value, TEnum flag) where TEnum : Enum
        {
            return value.HasFlag(flag);
        }
        #endregion

        public static string JoinAsString(this IEnumerable<string> source, string separator)
        {
            return string.Join(separator, source);
        }

        public static string JoinAsString<T>(this IEnumerable<T> source, string separator)
        {
            return string.Join(separator, source);
        }

        public static IEnumerable<T> GetValues<T>()
        {
            return Enum.GetValues(typeof(T)).Cast<T>();
        }

        public static TEnum Parse<TEnum>(object member)
        {
            string value = member.ToSafeString();
            TEnum result;
            if (string.IsNullOrWhiteSpace(value))
            {
                if (!typeof(TEnum).IsGenericType)
                {
                    throw new ArgumentNullException();
                }
                result = default;
            }
            else
            {
                result = (TEnum)Enum.Parse(typeof(TEnum), value, true);
            }
            return result;
        }

        public static string GetName<TEnum>(object member)
        {
            return GetName(typeof(TEnum), member);
        }

        public static string GetName(Type type, object member)
        {
            string result;
            if (type == null)
            {
                result = string.Empty;
            }
            else if (member == null)
            {
                result = string.Empty;
            }
            else if (member is string)
            {
                result = member.ToString();
            }
            else if (!type.IsEnum)
            {
                result = string.Empty;
            }
            else
            {
                result = Enum.GetName(type, member);
            }
            return result;
        }

        public static int GetValue<TEnum>(object member)
        {
            return GetValue(typeof(TEnum), member);
        }

        public static int GetValue(Type type, object member)
        {
            string value = member.ToSafeString();
            return (int)Enum.Parse(type, value, true);
        }

        public static T Parse<T>(string value)
        {
            IDictionary<string, string> descriptions = GetDescriptions<T>();
            T t = default;
            foreach (KeyValuePair<string, string> keyValuePair in descriptions)
            {
                if (keyValuePair.Key == value)
                {
                    t = (T)Enum.Parse(typeof(T), keyValuePair.Key);
                }
            }
            return t;
        }

        public static T FromDescription<T>(string Description)
        {
            IDictionary<string, string> descriptions = GetDescriptions<T>();
            T t = default;
            foreach (KeyValuePair<string, string> keyValuePair in descriptions)
            {
                if (keyValuePair.Value == Description)
                {
                    t = (T)Enum.Parse(typeof(T), keyValuePair.Key);
                }
            }
            return t;
        }

        public static string ToDescription<T>(T t)
        {
            IDictionary<string, string> descriptions = GetDescriptions<T>();
            string result = string.Empty;
            foreach (KeyValuePair<string, string> keyValuePair in descriptions)
            {
                if (keyValuePair.Key == t.ToString())
                {
                    result = keyValuePair.Value;
                }
            }
            return result;
        }

        public static IDictionary<string, string> GetDescriptions<T>()
        {
            Type type = typeof(T);
            if (!type.IsEnum)
            {
                throw new Exception(type.Name + "不是Enum类型");
            }
            return _dictDescriptions.GetOrAdd(type, delegate (Type t)
            {
                Dictionary<string, string> dictionary = new Dictionary<string, string>();
                foreach (object obj in Enum.GetValues(t))
                {
                    MemberInfo[] member = type.GetMember(obj.ToString());
                    object[] customAttributes = member[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
                    string value = customAttributes.Any() ? ((DescriptionAttribute)customAttributes[0]).Description : obj.ToString();
                    dictionary.Add(obj.ToString(), value);
                }
                return dictionary;
            });
        }

        private static ConcurrentDictionary<Type, IDictionary<string, string>> _dictDescriptions = new ConcurrentDictionary<Type, IDictionary<string, string>>();
    }
    [AttributeUsage(AttributeTargets.Field)]
    public class EnumOrderAttribute : Attribute
    {
        public readonly int Order;

        public EnumOrderAttribute(int order) => Order = order;
    }
}